const mqtt = require('mqtt')
const {
    getAuthInfo,
    log,
    validatorUrl,
    validatorDeviceName,
    strToHex,
} = require('../utils');
/**
 * 设备初始化
 *  mqtt 有自动重连机制不用特殊关注
 */
class MQTT {
    defaultConfig = {
        connectUrl: '', // mqtt服务器地址
        productKeys: ``, //
        deviceSecret: ``,
        connectTimeout: 4000, // 超时时间
        keepalive: 60, // 设备与物联网平台建立保活连接时的心跳间隔时间
        clean: true, // 是否清除session 默认是
    };
    config = {}; // 配置项
    device = null; // mqtt对象
    state = false; // mqtt 是否正常
    username = '';
    password = '';

    constructor(config, callback) {
        // 初始化配置对象
        this.config = { ...this.defaultConfig, ...config };
        this.init(callback);
        this.reconnect();
        this.close();
        // this.disconnect();
        this.offline();
        this.error();
        this.packetsend();
        this.packetreceive();
        this.connect();
        this.message();
    }

    init(callback) {
        let {
            connectUrl, // mqtt服务器地址
            deviceName, // 设备信息
            productKeys, //
            deviceSecret,
            connectTimeout, // 超时时间
            keepalive, // 设备与物联网平台建立保活连接时的心跳间隔时间
            clean,
        } = this.config;
        if (deviceName == '' || deviceName == null) {
            throw new Error('缺少设备名称');
        }
        if (connectUrl == '' || connectUrl == null) {
            throw new Error('缺少服务器地址');
        }
        if (productKeys == '' || productKeys == null) {
            throw new Error('缺少productKeys');
        }
        if (deviceSecret == '' || deviceSecret == null) {
            throw new Error('缺少deviceSecret');
        }
        const result = validatorUrl(connectUrl);
        if (!result) {
            throw new Error('url不合法');
        }
        const isValid = validatorDeviceName(deviceName);
        if (!isValid) {
            throw new Error('设备名字非法');
        }
        let authInfo = {};
        if (callback != void 0) {
            authInfo = callback(deviceName, productKeys, deviceSecret);
        } else {
            authInfo = getAuthInfo(deviceName, deviceSecret);
        }
        if (authInfo == void 0) {
            throw new Error('authInfo 错误');
        }
        if (authInfo.username == void 0 || authInfo.username == '') {
            throw new Error('username 错误');
        }
        if (authInfo.password == void 0 || authInfo.password == '') {
            throw new Error('password 错误');
        }
        this.username = authInfo.username;
        this.password = authInfo.password;
        const options = {
            clean, // 保留回话
            connectTimeout, // 超时时间
            clientId: deviceName,
            keepalive,
            username: this.username,
            password: this.password,
        };
        log.info('获取了设备信息[{}]', options);
        //TODO:  错误码
        this.device = mqtt.connect(connectUrl, options);
    }
    reconnect() {
        //当重新连接启动触发回调
        this.device.on('reconnect', () => {
            log.info('mqtt正在重连.....');
        });
    }

    close() {
        //连接断开后触发的回调
        this.device.on('close', function () {
            this.state = false;
            log.warn('客户端已断开连接.....');
        });
    }

    offline() {
        //客户端脱机下线触发回调
        this.device.on('offline', function () {
            this.state = false;
            log.warn('客户端脱机下线.....');
        });
    }

    error() {
        //当客户端无法连接或出现错误时触发回调
        this.device.on('error', (error) => {
            this.state = false;
            log.error('客户端出现错误[{}]:', error);
        });
    }

    packetsend() {
        //以下两个事件监听粒度细
        //当客户端发送任何数据包时发出。这包括published()包以及MQTT用于管理订阅和连接的包
        this.device.on('packetsend', (packet) => {
            let packetCopy = { ...{}, ...packet };
            delete packetCopy.payload;
            log.info('客户端已【发出】数据包[{}]:', packetCopy);
            if (packet.payload) {
                log.info('客户端已【发出】数据包payload为:');
                strToHex(packet.payload);
            }
        });
    }

    packetreceive() {
        //当客户端接收到任何数据包时发出。这包括来自订阅主题的信息包以及MQTT用于管理订阅和连接的信息包
        this.device.on('packetreceive', (packet) => {
            log.info('客户端已【接收】数据包:');
            strToHex(JSON.stringify(packet));
        });
    }
    
    connect() {
        //成功连接后触发的回调
        this.device.on('connect', function (connack) {
            // maqq 能正常通信
            this.state = true;
            log.info('成功连接上服务器可以正常通信了');
        });
    }
    //订阅某主题
    /**
     * device.subscribe(topic/topic array/topic object, [options], [callback])
     * topic:一个string类型的topic或者一个topic数组,也可以是一个对象
     * options
     */
    subscribe(topic, options = { qos: 0 }, callback) {
        log.info('订阅主题[{}] 参数信息为[{}]', topic, options);
        this.device.subscribe(topic, options, callback);
    }
    /**
     * 关闭mqtt
     */
    end() {
        this.device.end();
    }
    //发布数据
    /**
* device.publish(topic,message,[options], [callback])
 message: Buffer or String
* options:{
* qos:0, //默认0
* retain:false, //默认false
* dup:false, //默认false
* properties:{}
* }
* callback:function (err){}
*/
    publish(
        topic,
        options = {
            qos: 0, //默认0
            retain: false, //默认false
            dup: false, //默认false
            properties: {},
        },
        callback
    ) {
        log.info('客户端发布了数据[{}]:', topic);
        this.device.publish(topic, options, callback);
    }

    //当客户端接收到发布消息时触发回调
    /**
     * topic:收到的数据包的topic
     * message:收到的数据包的负载playload
     * packet:收到的数据包
     */
    message() {
        this.device.on('message', (topic, message, packet) => {
            log.info(
                '客户端【接收】订阅消息,topic=[{}];消息数据:[{}]',
                topic,
                message.toString(),
            );
        });
    }
}

module.exports = MQTT;
